﻿// --- Copyright (c) 2010 Steven Hartgers ---
// Copyright (C) 2010 Steven Hartgers All rights are reserved.
// Author contact: fiinix00/at\gmail.com
// ------------------------------------------------------------------------
// This program is free software: you can redistribute it and/or modify 
// it under the terms of the GNU General Public License as published by 
// the Free Software Foundation, either version 3 of the License, or 
// (at your option) any later version. 
//  
// This program is distributed in the hope that it will be useful, 
// but WITHOUT ANY WARRANTY; without even the implied warranty of 
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
// GNU General Public License for more detail
//  
// You should have received a copy of the GNU General Public License 
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
// ------------------------------------------------------------------------
// COPYRIGHT NOTICE: This copyright notice may NOT be removed, obscured or modified 
// without written consent from the author.
// --- End copyright notice --- 


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

using Hack.Func;

namespace Hack.Func
{
    public static class TryCatch
    {
        public static Action Catch<T>(this Action body, Action<T> handler) where T : Exception
        {
            return Catch<T>(body, ex => true, handler);
        }

        public static Action Catch<T>(this Action body, Func<T, bool> filter, Action<T> handler) where T : Exception
        {
            return delegate
            {
                try
                {
                    body();
                }
                catch (T ex)
                {
                    if (!filter(ex))
                        throw;
                    else
                        handler(ex);
                }
            };
        }
        public static Action Finally(this Action body, Action @finally)
        {
            return delegate
            {
                try
                {
                    body();
                }
                finally
                {
                    @finally();
                }
            };
        }
        public static Action Using(this IDisposable resource, Action block)
        {
            return block.Finally(delegate
            {
                if (resource != null)
                    resource.Dispose();
            });
        }
    }

    public static class Loops
    {
        public static void While(Func<bool> condition, Action body)
        {
            while (condition()) body();
        }
    }
    /// <summary>
    ///     try
    ///     {
    ///         Faults.Fault(
    ///             () => Console.WriteLine("Running fault1"),
    ///             () => Console.WriteLine("Running fault1, got error")
    ///         )();
    ///     }
    ///     catch (Exception ex) { Console.WriteLine(ex); }
    /// </summary>
    public class Faults
    {
        public static Action Fault(Action protectedBlock, Action faultHandler)
        {
            return () => Faulter.Fault(protectedBlock, faultHandler);
        }

        private class Faulter : IDisposable
        {
            private Action faultHandler;

            public Faulter(Action faultHandler)
            {
                this.faultHandler = faultHandler;
            }

            public void Dispose()
            {
                faultHandler();
            }

            public static void Fault(Action protectedBlock, Action faultHandler)
            {
                GetFaultEnumerator(protectedBlock, faultHandler).MoveNext();
            }

            private static IEnumerator GetFaultEnumerator(Action protectedBlock, Action faultHandler)
            {
                using (var f = new Faulter(faultHandler))
                {
                    protectedBlock();
                    yield return false;
                }
            }
        }
    }

    interface IRecord<T1, T2>// : System.Collections.Generic.IEnumerable<T>
    {
        T1 First { get; }
        T2 Second { get; }
    }
    public class Counter : IRecord<Func<int>, Action<int>>
    {

        // Data
        private int _value;

        // Code - explicit implementation to hide First, Second
        Func<int> IRecord<Func<int>, Action<int>>.First { get { return () => _value; } }
        Action<int> IRecord<Func<int>, Action<int>>.Second { get { return i => _value += i; } }

        // Code - friendly "interface"
        public Func<int> Get { get { return ((IRecord<Func<int>, Action<int>>)this).First; } }
        public Action<int> Inc { get { return ((IRecord<Func<int>, Action<int>>)this).Second; } }
    }

    /// <summary>
    /// Orginal Author: Bart de smet : http://bartdesmet.net/blogs/
    /// http://bartdesmet.net/blogs/bart/archive/2008/03/30/a-functional-c-type-switch.aspx
    /// </summary>
    public class Switch
    {
        public Switch(object o)
        {
            Object = o;
        }

        public object Object { get; private set; }
    }
    public class Switch<T>
    {
        public Switch(T o)
        {
            Object = o;
        }
        public T Object { get; private set; }
    }
    public class Switch<T, R>
    {
        public Switch(T o)
        {
            Object = o;
        }

        public T Object { get; private set; }
        public bool HasValue { get; private set; }
        public R Value { get; private set; }

        public void Set(R value)
        {
            Value = value;
            HasValue = true;
        }
    }

    public static class SwitchExtension
    {
        public static Switch<T, R> Case<T, R>(this Switch<T, R> s, T t, Func<T, R> f)
        {
            return Case<T, R>(s, x => object.Equals(x, t), f);
        }
        public static Switch<T, R> Case<T, R>(this Switch<T, R> s, Func<T, bool> c, Func<T, R> f)
        {
            if (!s.HasValue && c(s.Object))
            {
                s.Set(f(s.Object));
            }

            return s;
        }
        public static R Default<T, R>(this Switch<T, R> s, Func<T, R> f)
        {
            if (!s.HasValue)
            {
                s.Set(f(s.Object));
            }

            return s.Value;
        }


        public static Switch Case<T>(this Switch s, Action<T> a) where T : class
        {
            return Case<T>(s, o => true, a, false);
        }
        public static Switch Case<T>(this Switch s, Action<T> a, bool fallThrough) where T : class
        {
            return Case<T>(s, o => true, a, fallThrough);
        }
        public static Switch Case<T>(this Switch s, Func<T, bool> c, Action<T> a) where T : class
        {
            return Case<T>(s, c, a, false);
        }
        public static Switch Case<T>(this Switch s, Func<T, bool> c, Action<T> a, bool fallThrough) where T : class
        {
            if (s == null)
            {
                return null;
            }
            else
            {
                T t = s.Object as T;
                if (t != null)
                {
                    if (c(t))
                    {
                        a(t);
                        return fallThrough ? s : null;
                    }
                }
            }

            return s;
        }


        public static Switch Case(this Switch s, object o, Action<object> a)
        {
            return Case(s, o, a, false);
        }
        public static Switch Case(this Switch s, object o, Action<object> a, bool fallThrough)
        {
            return Case(s, x => object.Equals(x, o), a, fallThrough);
        }
        public static Switch Case(this Switch s, Func<object, bool> c, Action<object> a)
        {
            return Case(s, c, a, false);
        }
        public static Switch Case(this Switch s, Func<object, bool> c, Action<object> a, bool fallThrough)
        {
            if (s == null)
            {
                return null;
            }
            else if (c(s.Object))
            {
                a(s.Object);
                return fallThrough ? s : null;
            }

            return s;
        }
        public static void Default(this Switch s, Action<object> a)
        {
            if (s != null)
            {
                a(s.Object);
            }
        }


        public static Switch<T> Case<T>(this Switch<T> s, T t, Action<T> a)
        {
            return Case(s, t, a, false);
        }
        public static Switch<T> Case<T>(this Switch<T> s, T t, Action<T> a, bool fallThrough)
        {
            return Case(s, x => object.Equals(x, t), a, fallThrough); //Case(s, x => object.Equals(x, t), a, fallThrough);
        }
        public static Switch<T> Case<T>(this Switch<T> s, Func<T, bool> c, Action<T> a)
        {
            return Case(s, c, a, false);
        }
        public static Switch<T> Case<T>(this Switch<T> s, Func<T, bool> c, Action<T> a, bool fallThrough)
        {
            if (s == null)
            {
                return null;
            }
            else if (c(s.Object))
            {
                a(s.Object);
                return fallThrough ? s : null;
            }

            return s;
        }
        public static void Default<T>(this Switch<T> s, Action<T> a)
        {
            if (s != null)
            {
                a(s.Object);
            }
        }
    }
}